#include "Coroutine.h"

Coroutine::Coroutine(const std::function<void(YieldContext&)> &func)
: context_(nullptr)
, executor_(nullptr)
{
    executor_ = new boost::coroutines2::coroutine<void>::push_type(
        [=](boost::coroutines2::coroutine<void>::pull_type &yield) {
        TRY_BEGIN {
            context_ = new YieldContext(*this, yield);
            func(*context_);
        } TRY_END
        CATCH_BEGIN(const boost::context::detail::forced_unwind &) {
            throw;
        } CATCH_END
        CATCH_BEGIN(const IException &e) {
            e.Print();
        } CATCH_END
        CATCH_BEGIN(...) {
        } CATCH_END
    });
}

Coroutine::~Coroutine()
{
    delete context_;
    delete executor_;
}

void Coroutine::OnRPCInvokeResp(INetStream &pck, int32 err, bool eof)
{
    RPCInvokeResp resp{&pck, err, eof};
    context_->rpc_invoke_resp_ = &resp;
    (*executor_)();
}

void Coroutine::Spawn(const std::function<void(YieldContext&)> &func)
{
    auto coroutine = std::make_shared<Coroutine>(func);
    (*coroutine->executor_)();
}

Coroutine::YieldContext::YieldContext(Coroutine &coroutine,
    boost::coroutines2::coroutine<void>::pull_type &yield)
: coroutine_(coroutine)
, yield_(yield)
{
}

std::function<void(INetStream&, int32, bool)> Coroutine::YieldContext::GetRPCInvokeCb()
{
    return std::bind(&Coroutine::OnRPCInvokeResp, coroutine_.shared_from_this(),
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}

const Coroutine::RPCInvokeResp &Coroutine::YieldContext::WaitRPCInvokeResp()
{
    yield_();
    return *rpc_invoke_resp_;
}
